#ifndef GRAPH_OBJECT_H
#define GRAPH_OBJECT_H

#include "GUI.h"

namespace GUI
{
	namespace AspectRatioMode
	{
		enum
		{
			AdjustNone,
			AdjustWidth,
			AdjustHeight,
			AdjustBoth,
		};
	}

	//Forward decalration
	class GraphDisplay;

	class GraphObject
	{
	public:
		struct CompareByZOrder;
		friend struct GraphObject::CompareByZOrder;

		enum
		{
			//top to bottom
			MessageZOrder,
			StatusBarZOrder,
			TextStimulusZOrder,
			ImageStimulusZOrder,
			ShapeZOrder,
			SceneDisplayZOrder,
		};

	private:
		enum
		{//Flags for properties that may have changed
			All = -1,
			Position = 1 << 0,
			Size = 1 << 1,
		};


	public:
		GraphObject(GraphDisplay&, float zorder);
		virtual ~GraphObject();

		//properties
	public:
		const GraphDisplay& Display() const
		{ return mDisplay; }

		GraphObject& Hide()
		{
			if(mVisible) Invalidate();
			mVisible = false;
			return *this;
		}
		GraphObject& Show()
		{
			if(!mVisible) Invalidate();
			mVisible = true;
			return *this;
		}
		bool Visible() const
		{ return mVisible; }

		GraphObject& SetZOrder(float zorder)
		{
			Invalidate();
			mZOrder = zorder;
			return *this;
		}
		float ZOrder() const
		{ return mZOrder; }

		GraphObject& SetAspectRationMode(int m)
		{
			Invalidate();
			mAspectRatioMode = m;
			Change();
			return *this;
		}
		int AspectRatioMode() const
		{ return mAspectRatioMode; }

		// Properties that describe the object's position/extension on screen.
		// All rectangles are given in normalized coordinates.
		//  The ObjectRect property is the object's rectangle as described by the user.
		//  For auto-sizing objects (AspectRatioMode==Adjust*), it may be an empty rectangle.
		GraphObject& SetObjectRect(const GUI::Rect&);
		const GUI::Rect& ObjectRect() const
		{ return mObjectRect; }
		//  The read-only BoundingRect property describes the object's actual extent on screen.
		GUI::Rect BoundingRect() const;

		virtual bool NeedsGL() const
		{ return false; }

		//Graphics
		GraphObject& Invalidate();

		//Events
		// calling side
		void Paint();
		void Change(int which = All);
		bool Click(const Point&);

	protected:
		// Handling side
		// this function implements drawing the object.
		virtual void OnPaint(const DrawContext&) = 0;
		// This cuntion is called when a change of properties other than position or size occurs.
		virtual void OnChange(DrawContext&){}
		// These function are called when position or size has changed.
		// They call OnChange() bydefault, so you need not implement them.
		// For AspectRationModes that adapt the object's enclosing rectangle,
		// OnMove() and OnSize() must change the DrawContext's rectangle to reflect adaptaion.
		virtual void OnMove(DrawContext& dc)
		{ OnChange(dc); }
		virtual void OnResize(DrawContext& dc)
		{ OnChange(dc); }
		//  The OnClick event handler receives a point in continuous coordinates, and
		//  returns whether it considers itself clicked.
		virtual bool OnClick(const Point& )
		{ return true; }

	public:
		// sort order for drawing (smaller number corrspongd to top)
		struct CompareByZOrder{
			bool operator()(const GraphObject* s1, const GraphObject* s2) const
			{
				return (s1->mZOrder == s2->mZOrder ? s1 > s2 : s1->mZOrder > s2->mZOrder);
			}
		};
	private:
		GraphDisplay&	mDisplay;
		bool			mVisible;
		bool			mRectSet;
		float			mZOrder;
		int				mAspectRatioMode;
		GUI::Rect		mObjectRect; // stored in normalized coordinates
		GUI::Rect		mBoundingRect;	// stored in pixel coordinates
	};
}

#endif